package game;

import java.util.Arrays;

import menu.MenuController;
import player.Player;
import player.PlayerController;
import adapter.Adapter;
import dice.DiceCup;
import field.FieldController;
import field.Ownable;
import field.Property;

public class Game {

	private PlayerController pC;
	private FieldController fC;
	private DiceCup diceC;
	private MenuController mC;
	private Adapter adapter;
	private int[] playerData, fieldData;
	private int maxRounds;
	private boolean phaseTwo, game;

	public Game(PlayerController playerC, FieldController fieldC){
		pC = playerC;
		fC = fieldC;
		mC = new MenuController();
		adapter = new Adapter();
		diceC = new DiceCup();
		playerData = new int[3];
		fieldData = new int[3];
		maxRounds = -1;
	}

	public void startSpil(String rounds){
		if (checkRounds(rounds) == true){
			// Hvis der er sat et maks på antal runder der skal spilles
			maxRounds = Integer.parseInt(rounds);
		}
		game(); // Main controller startes
	}

	/**
	 * Tjekker hvorvidt en string er en integer eller ej.
	 * @param rounds String der skal tjekkes
	 * @return Resultatet af tjekket (sandt/falsk)
	 */
	private boolean checkRounds(String rounds){
		try { 
			Integer.parseInt(rounds); 
		} catch(NumberFormatException e) { 
			return false; 
		}
		return true;
	}

	/**
	 * Køres når der landes på et luck-felt hvor man bliver flyttet nogle positioner
	 * @param p Spilleren
	 * @param sum Antal felter man er blevet flyttet
	 */
	private void roundLight(Player p, int sum){
		do {
			fieldData = fC.landOnField(p.getPosition(), p, sum); 
			playerData[0] = fieldData[0];
			playerData[1] = fieldData[1];
			playerData[2] = fieldData[2];
			pC.updPlayer(p, playerData); // Opdater spillerens data efter fase1
			resetPlayerData(p); // Nulstiller data efter det er opdateret
		}
		while (playerData[1] != fieldData[1]);
	}

	/**
	 * Kører en fase1 runde for spilleren
	 * @param p Spilleren der har en tur
	 */
	private void round(Player p){
		int k=0;
		do {
			// Runde kører og opdaterer spillerens data
			adapter.rollDice(p.getName());
			diceC.rollDice();
			adapter.setDice(diceC.getDice()[0], 0, 4, 3, diceC.getDice()[1], 0, 6, 3); // Viser terningeslaget på interface
			if (diceC.equal()==true){ 
				k++;
			}
			adapter.removeCar(playerData[1]+1, p.getName());
			if (k<3){
				// Spillerens placering opdateres (0-39)
				if(playerData[1] + diceC.sum() >= 40 ){
					playerData[1] = diceC.sum()-((40) - playerData[1]);
					playerData[0] = playerData[0] + 4000; // Man får 4000 for at passere Start
				}
				else{
					playerData[1] = playerData[1] + diceC.sum(); 
				}

				// Flytter spillerens bil
				adapter.setCar(playerData[1]+1, p.getName());
				pC.updPlayer(p, playerData); // Opdater spillerens data efter fase1
				resetPlayerData(p); // Nulstiller data efter det er opdateret

				int oldPosition = p.getPosition();
				// LanOnField kaldes og playerData opdateres med data fra fieldController
				fieldData = fC.landOnField(p.getPosition(), p, diceC.sum()); 
				playerData[0] = fieldData[0];
				playerData[1] = fieldData[1];
				playerData[2] = fieldData[2];
				int newPos = fieldData[1];

				pC.updPlayer(p, playerData); // Opdater spillerens data efter fase1
				resetPlayerData(p); // Nulstiller data efter det er opdateret

				if (oldPosition != newPos && p.getInJail() == false){
					int sum = fieldData[1] - oldPosition;
					if (sum < 0){
						sum = sum+40;
					}
					roundLight(p, sum);
				}
			}
			else {
				playerData[0] = 0;
				playerData[1] = fC.moveToJail();
				adapter.setCar(playerData[1]+1, p.getName());
				playerData[2] = 1;
				pC.updPlayer(p, playerData); // Opdater spillerens data efter fase1
				resetPlayerData(p); // Nulstiller data efter det er opdateret
			}
		} while (diceC.equal()==true && p.getInJail()==false);
	}

	/**
	 * Kører spillet (Main Controller funktion)
	 */
	private void game(){
		game = true; // Spillet kører så længe game er true
		Player[] p = pC.getPlayers(); // Henter player-array
		int n;
		//		fC.snyd(p[0]); // TESTKODE SKAL FJERNES!!!!
		//		playerData = fC.landOnField(4, p[0], 4); // TESTKODE
		//		pC.updPlayer(p[0], playerData); // TESTKODE
		//		fC.bankruptcy(p[0]); // TESTKODE

		while (game == true){
			if (maxRounds >= 0){
				adapter.updRounds(maxRounds);				
			}
			n = 0;
			for (n=0; n < p.length; n++){
				resetPlayerData(p[n]); // Først 'nulstilles' playerData så data matcher spiller p[n] (nuværende spiller)

				if (p[n].getInJail() == true){ 
					jail(p[n]); // Hvis spilleren er i fængsel
				}
				else { 
					round(p[n]); // Hvis spilleren ikke er i fængsel køres en normal runde
				}
				phaseTwo = true;
				while (phaseTwo == true){
					phaseTwo(p[n]);
					pC.updPlayer(p[n], playerData); // Opdater spillerens data efter fase2
					resetPlayerData(p[n]); // Nulstiller data efter det er opdateret
				}
			}
			p = playerUpd(p);

			if (maxRounds > 0){
				maxRounds = maxRounds -1;				
			}
			else if (maxRounds == 0){
				game = false;
			}
		}
		winner(p);
	}

	/**
	 * Kører en fase2 menurunde for en specifik spiller
	 * @param player Spilleren
	 */
	private void phaseTwo(Player player) {
		int[] menuData = mC.menu(player, fC.getFields(), pC.getPlayers());
		if (menuData[0] != 0){
			playerData[0] = fC.buyHouse(menuData[0]); // Der er købt et hus
		}
		else if (menuData[1] != 0){
			playerData[0] = fC.sellHouse(menuData[1]); // Der sælges et hus
		}
		else if (menuData[2] != 0){
			playerData[0] = fC.pawn(menuData[2]); // Grunden pantsættes
		}
		else if (menuData[3] != 0){
			playerData[0] = fC.unPawn(menuData[3]); // Pantsætning af grund ophæves
		}
		else if (menuData[4] == 0){ // Fortryd
			phaseTwo = false;
		}
		else if (menuData[6] != 0){ // Der er solgt et felt
			playerData[0] = menuData[7]; // Opdaterer sælgers balance med prisen
			Player[] players = pC.getPlayers();
			
			int[] tempData = new int[3];
			tempData[0] = -menuData[7]; // Købers balance fratrækkes prisen
			tempData[1] = players[menuData[5]].getPosition();
			tempData[2] = players[menuData[5]].getJailCount();
			
			pC.updPlayer(players[menuData[5]], tempData);
			fC.sellGround(menuData[6], players[menuData[5]]);
		}
	}

	/**
	 * Nulstiller data i playerData når der skiftes tur
	 * @param p Den spiller der lige har fået sin tur
	 */
	private void resetPlayerData(Player p){
		playerData[0] = 0;
		playerData[1] = p.getPosition();
		if (p.getInJail() == true){
			playerData[2] = 1;
		}
		else {
			playerData[2] = 0;
		}
	}

	/**
	 * Håndterer tilfældet hvor den nuværende spiller er i fængsel
	 * @param p Spilleren der er i fængsel
	 */
	private void jail(Player p){
		// Spilleren skal slå et slag
		int button = adapter.jailMenu(p.getName());
		switch(button){
		case 0:	{
			adapter.rollDice(p.getName());
			diceC.rollDice();
			adapter.setDice(diceC.getDice()[0], 0, 4, 3, diceC.getDice()[1], 0, 6, 3); // Viser terningeslaget på interface
			if (diceC.equal()==true){
				p.setInJail(false);
				adapter.removeCar(playerData[1]+1, p.getName());
				playerData[1] = playerData[1] + diceC.sum(); 

				// Flytter spillerens bil
				adapter.setCar(playerData[1]+1, p.getName());
				pC.updPlayer(p, playerData); // Opdater spillerens data efter fase1
				resetPlayerData(p); // Nulstiller data efter det er opdateret

				// LanOnField kaldes og playerData opdateres med data fra fieldController
				fieldData = fC.landOnField(p.getPosition(), p, diceC.sum()); 
				playerData[0] = fieldData[0];
				playerData[1] = fieldData[1];
				playerData[2] = fieldData[2];

				round(p);
			}
			else {
				if(p.getJailCount()<2){
					p.setJailCount(p.getJailCount()+1);
				}
				else if(p.getJailCount()==2){
					p.setInJail(false);
					p.setJailCount(0);
					playerData[0] = playerData[0]-1000;

					adapter.removeCar(playerData[1]+1, p.getName());
					playerData[1] = playerData[1] + diceC.sum(); 

					// Flytter spillerens bil
					adapter.setCar(playerData[1]+1, p.getName());
					pC.updPlayer(p, playerData); // Opdater spillerens data efter fase1
					resetPlayerData(p); // Nulstiller data efter det er opdateret

					// LanOnField kaldes og playerData opdateres med data fra fieldController
					fieldData = fC.landOnField(p.getPosition(), p, diceC.sum()); 
					playerData[0] = fieldData[0];
					playerData[1] = fieldData[1];
					playerData[2] = fieldData[2];
				}
			}
		}
		break;
		case 1:	{
			p.setInJail(false);
			p.setJailCount(0);
			playerData[0] = playerData[0]-1000;
			playerData[2] = 0;
			pC.updPlayer(p, playerData); // Opdater spillerens data efter fase1
			resetPlayerData(p);
			round(p);
		}
		break;
		default:
		}
	}

	/**
	 * Annoncerer en vinder (eller to i tilfælde af uafgjort).
	 */
	public void winner(Player[] p){
		if(p.length>1){ // Hvis de er mere end én vinder så spillet skal ende uafgjort
			int[] formue = new int[p.length];
			for(int n = 0;n<p.length;n++){
				int value = p[n].getAccount().getBalance();		
				for (int i = 0; i < fC.getFields().length; i++) {
					if (fC.getFields()[i] instanceof Ownable){
						Player owner = ((Ownable) fC.getFields()[i]).getOwner();
						if (owner == p[n]){
							value = value + ((Ownable) fC.getFields()[i]).getPawnPrice();
							if (fC.getFields()[i] instanceof Property){
								value = value + ((Property) fC.getFields()[i]).getBuyBuildingPrice() * ((Property) fC.getFields()[i]).getHouseCount();
							}
						}
					}
				}
				p[n].getAccount().setBalance(value);
				adapter.setBalance(p[n].getName(), p[n].getAccount().getBalance());
				formue[n] = p[n].getAccount().getBalance();				
			}
			Arrays.sort(formue);
			for (int i=0; i<p.length; i++){
				if(p[i].getAccount().getBalance()==formue[formue.length-1]){ // Er spillerens balance lig den vindende balance flyttes spilleren til første position i arrayet.
					p[0]=p[i];
				}
			}
			adapter.winner(p[0].getName());
		}
		else if(p.length<=1){ // Der er fundet en vinder
			adapter.winner(p[0].getName());
		}
	}

	/**
	 * Tjekker hvorvidt en spiller har vundet eller er gået bankerot.
	 */
	public Player[] playerUpd(Player[] k){

		for(int i = 0;i<k.length;i++){
			do{
				if(k[i].getAccount().getBalance()<0){
					fC.bankruptcy(k[i]); // Hvis spilleren er bankerot
					k = updArray(k, i); // Opdaterer spillerarrayet
				}
			}
			while(k[i].getAccount().getBalance()<0);
		}
		if(k.length<=1){
			game=false;
		}
		return k;
	}

	/**
	 * Fjerner en spiller fra array af spillere når spilleren er bankerot
	 * @param a Spilleren der skal fjernes fra arrayet
	 * @param index Spillerens nummer/placering i arrayet
	 * @return Det nye spiller-array
	 */
	public Player[] updArray(Player[] a, int index){
		int num = a.length - (index + 1) ;
		System.arraycopy(a, index + 1, a, index, num);
		Player[] p = new Player[a.length-1];
		System.arraycopy(a, 0, p, 0, p.length);
		return p;
	}
}